package gitbucket.core.util

import java.io.ByteArrayInputStream
import scala.jdk.CollectionConverters._
import gitbucket.core.model.Profile._
import gitbucket.core.model.Profile.profile.blockingApi._
import org.bouncycastle.bcpg.ArmoredInputStream
import org.bouncycastle.openpgp.{PGPPublicKey, PGPPublicKeyRing, PGPSignatureList}
import org.bouncycastle.openpgp.bc.BcPGPObjectFactory
import org.bouncycastle.openpgp.operator.bc.BcPGPContentVerifierBuilderProvider

object GpgUtil {
  def str2GpgKeyId(keyStr: String): Option[Long] = {
    val pubKeyOf = new BcPGPObjectFactory(new ArmoredInputStream(new ByteArrayInputStream(keyStr.getBytes)))
    pubKeyOf.iterator().asScala.collectFirst {
      case keyRing: PGPPublicKeyRing =>
        keyRing.getPublicKey().getKeyID
    }
  }

  def getGpgKey(gpgKeyId: Long)(implicit s: Session): Option[PGPPublicKey] = {
    val pubKeyOpt = GpgKeys.filter(_.byGpgKeyId(gpgKeyId)).map { _.publicKey }.firstOption
    pubKeyOpt.flatMap { pubKeyStr =>
      val pubKeyObjFactory =
        new BcPGPObjectFactory(new ArmoredInputStream(new ByteArrayInputStream(pubKeyStr.getBytes())))
      pubKeyObjFactory.nextObject() match {
        case pubKeyRing: PGPPublicKeyRing =>
          Option(pubKeyRing.getPublicKey(gpgKeyId))
        case _ =>
          None
      }
    }
  }

  def verifySign(signInfo: JGitUtil.GpgSignInfo)(implicit s: Session): Option[JGitUtil.GpgVerifyInfo] = {
    try {
      new BcPGPObjectFactory(new ArmoredInputStream(new ByteArrayInputStream(signInfo.signArmored)))
        .iterator()
        .asScala
        .flatMap {
          case signList: PGPSignatureList =>
            signList
              .iterator()
              .asScala
              .flatMap { sign =>
                getGpgKey(sign.getKeyID)
                  .map { pubKey =>
                    sign.init(new BcPGPContentVerifierBuilderProvider, pubKey)
                    sign.update(signInfo.target)
                    (sign, pubKey)
                  }
                  .collect {
                    case (sign, pubKey) if sign.verify() =>
                      JGitUtil.GpgVerifyInfo(pubKey.getUserIDs.next, pubKey.getKeyID.toHexString.toUpperCase)
                  }
              }
        }
        .toList
        .headOption
    } catch {
      case _: Throwable => None
    }

  }
}
